package com.github.xzb617.security.client;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.xzb617.common.util.StrUtil;
import com.github.xzb617.domain.common.ErrorResponse;
import com.github.xzb617.domain.common.ErrorStatus;
import com.github.xzb617.props.SecurityProperties;
import com.github.xzb617.utils.ResponseUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 客户端认证过滤器
 * <p>
 *     1.如果服务端同时配置了Key 和 Secret，则要求客户端在请求头中携带这两个参数
 *     2.客户端可直接在配置文件中配置以上两个值即可
 * </p>
 * @author xzb617
 */
@Component
public class ClientInterceptor implements HandlerInterceptor {

    @Value("${server.servlet.context-path}")
    private String servletContextPath;
    private final String clientAccessKey;
    private final String clientAccessSecret;
    private final ObjectMapper objectMapper;

    // 路径匹配器
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    public ClientInterceptor(SecurityProperties securityProperties, ObjectMapper objectMapper) {
        this.clientAccessKey = securityProperties.getClientAccessKey();
        this.clientAccessSecret = securityProperties.getClientAccessSecret();
        this.objectMapper = objectMapper;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 是否配置了Key、Secret
        if (StrUtil.isEmpty(clientAccessKey) || StrUtil.isEmpty(clientAccessSecret)) {
            return true;
        }

        // 匹配客户端访问路径
        String uri = this.getEffectRequestURI(request);
        boolean clientReqMatched = this.antPathMatcher.match("/clients/**", uri);
        if (!clientReqMatched) {
            return true;
        }

        // 获取客户端的Key、Secret
        String accessKey = request.getHeader("client-access-key");
        String accessSecret = request.getHeader("client-access-secret");

        // 认证失败
        if (!clientAccessKey.equals(accessKey) || !clientAccessSecret.equals(accessSecret)) {
            writeUnauthorizedResponse(response);
            return false;
        }

        return true;
    }

    private void writeUnauthorizedResponse(HttpServletResponse response) throws IOException {
        String respJson = this.objectMapper.writeValueAsString(new ErrorResponse(ErrorStatus.UNAUTHORIZED, "客户端认证失败，缺少必须的认证信息"));
        ResponseUtil.writeJSON(response, HttpStatus.UNAUTHORIZED, respJson);
    }

    /**
     * 获取 ServletContextPath 之后的请求路径
     * @param request
     * @return
     */
    private String getEffectRequestURI(HttpServletRequest request) {
        // 解析
        String effectURI = request.getRequestURI();
        if (servletContextPath != null) {
            effectURI = effectURI.replace(servletContextPath, "");
        }
        return effectURI;
    }
}
